////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include "ConvexHull.hpp"

#include <assert.h>
#include <fstream>
#include <iostream>
#include <string>

#include <SFML/OpenGL.hpp>

#include <SmegTools/Tools.hpp>

#include <SmegTools/Debug/DebugNew.hpp>

namespace smeg {
    namespace light {
        
////////////////////////////////////////////////////////////
/// Is the given hull convex
////////////////////////////////////////////////////////////
bool ConvexHull::IsValidConvexHull( const sf::VertexArray& _VertexArray ) {
    // TODO With new LightSystem, ConcaveHull WORKS!!!
    // Problem is the renderiong of a ConcaveHull...

    unsigned numVertices = _VertexArray.getVertexCount(),
        lastVertex,
        nextVertex;
    sf::Vector2f lastVector, nextVector;
    float curCross;

    for ( unsigned curVertex = 0; curVertex < numVertices; ++curVertex ) {
        lastVertex = tools::Wrap(curVertex - 1, numVertices);
        nextVertex = tools::Wrap(curVertex + 1, numVertices);
        
        lastVector = _VertexArray[lastVertex].position - _VertexArray[curVertex].position;
        nextVector = _VertexArray[nextVertex].position - _VertexArray[curVertex].position;
        
        curCross = math::Cross( lastVector, nextVector );

        // The angle between lastVector and nextVector must be >= 0.0f and <= 180.0f
        if ( curCross > 0.0f ) {
            return false;
        }
    }

    return true;
}

////////////////////////////////////////////////////////////
/// Default Constructor
////////////////////////////////////////////////////////////
ConvexHull::ConvexHull( const sf::Vector2f& _Center, 
                        float _fAngleDeg, 
                        bool _bIsStatic,
                        ShadowCasterEntity* _pParent ) :
    Hull( _Center, _fAngleDeg, _bIsStatic, _pParent )
{
}
    
////////////////////////////////////////////////////////////
/// Constructor with a filename to load
////////////////////////////////////////////////////////////
ConvexHull::ConvexHull( const std::string& _Filename, 
                        const sf::Vector2f& _Center, 
                        float _fAngleDeg,
                        bool _bIsStatic,
                        ShadowCasterEntity* _pParent ) :
    Hull( _Center, _fAngleDeg, _bIsStatic, _pParent )
{
    LoadShape( _Filename );
}
    
////////////////////////////////////////////////////////////
/// Constructor with an array of vertices
////////////////////////////////////////////////////////////
ConvexHull::ConvexHull( const sf::VertexArray& _VertexArray, 
                        float _fSizeMultiplier, 
                        const sf::Vector2f& _Center, 
                        float _fAngleDeg,
                        bool _bIsStatic,
                        ShadowCasterEntity* _pParent ) :
    Hull( _Center, _fAngleDeg, _bIsStatic, _pParent )
{
    SetupShape( _VertexArray, _fSizeMultiplier );
}
    
////////////////////////////////////////////////////////////
/// Copy Constructor
////////////////////////////////////////////////////////////
ConvexHull::ConvexHull( const ConvexHull& _Copy ) :
    Hull( _Copy )
{
}
    
////////////////////////////////////////////////////////////
/// Destructor
////////////////////////////////////////////////////////////
ConvexHull::~ConvexHull()
{
}

////////////////////////////////////////////////////////////
/// Load a convex hull from a file
////////////////////////////////////////////////////////////
bool ConvexHull::LoadShape( const std::string& fileName )
{
    std::ifstream fileLoaded( fileName );
    bool loadingSuccess = false;

    if( fileLoaded.good() ) {
        unsigned uCurVert = 0;

        Clear();

        fileLoaded >> m_uNumVertices;

        m_pOriginalVertices = new sf::Vector2f[m_uNumVertices];
        while( !fileLoaded.eof() )
        {
            fileLoaded >> m_pOriginalVertices[uCurVert].x
                >> m_pOriginalVertices[uCurVert].y;

            ++uCurVert;
        }

        ComputeHull();

        loadingSuccess = true;
    } else {
        std::cout << "Could not load hull \"" << fileName << "\"!" << std::endl;
    }

    fileLoaded.close();

    return loadingSuccess;
}

////////////////////////////////////////////////////////////
/// Setup a convex hull from an array of vertices
////////////////////////////////////////////////////////////
void ConvexHull::SetupShape( const sf::VertexArray& _VertexArray, float _fSizeMultiplier ) {
    Clear();

    m_uNumVertices = _VertexArray.getVertexCount();
    m_pOriginalVertices = new sf::Vector2f[m_uNumVertices];
    for ( unsigned i = 0; i < m_uNumVertices; ++i ) {
        m_pOriginalVertices[i] = _VertexArray[i].position * _fSizeMultiplier;
    }

    ComputeHull();
}

////////////////////////////////////////////////////////////
/// Is the given point inside the hull
////////////////////////////////////////////////////////////
bool ConvexHull::IsPointInHull( const sf::Vector2f& _Point ) const {
    int iNeighbour,
        iSgn = 0, 
        iSgnCross;
    float fCross;
    // Adjust p to the hull local space
    sf::Vector2f pLocal( _Point - m_Center ),
        edge,
        pToVert;

    for ( unsigned i = 0; i < m_uNumVertices; ++i ) {
        iNeighbour = tools::Wrap( i + 1, m_uNumVertices );
        edge = m_pVertices[iNeighbour] - m_pVertices[i];
        pToVert = pLocal - m_pVertices[i];

        // We only care about the sign of the cross product
        // In order for the point to be in the hull,
        // the cross product between pToVert and edge 
        // need to be of the same sign (- or +) that the one of the other cross products
        fCross = math::Cross( pToVert, edge );
        iSgnCross = static_cast<int>( fCross / abs( fCross ) );

        if ( 0 == iSgn )
            iSgn = iSgnCross;
        else if ( iSgn != iSgnCross )
            return false;
    }

    return true;
}

    } // namespace light
} // namespace smeg